#!/bin/bash

#
# BBBEasy open source platform - https://riadvice.tn/
#
# Copyright (c) 2022-2024 RIADVICE SUARL and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BBBeasy is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License along
# with BBBEasy; if not, see <http://www.gnu.org/licenses/>.
#
#
#
# Author(s):
#       Ghazi Triki <ghazi.triki@riadvice.tn>
#
# Changelog:
#   2022-02-06 GTR Initial Version

# The real location of the script
SCRIPT=$(readlink -f "$0")

# Current unix username
USER=$(whoami)

# Directory where the script is located
BASEDIR=$(dirname "$SCRIPT")

# Formatted current date
NOW=$(date +"%Y-%m-%d_%H.%M.%S")

# Production BBBEasy directory
APP_DIR=$BASEDIR/../

# Current git branch name transforms '* dev-0.5' to 'dev-0.5'
GIT_BRANCH=$(git --git-dir="$BASEDIR/../.git" branch | sed -n '/\* /s///p')

# Git tag, commits ahead & commit id under format '0.4-160-g3bb256c'
GIT_VERSION=$(git --git-dir="$BASEDIR/../.git" describe --tags --always HEAD)

#
# Display usage
#
usage() {
  echo
  echo "#========================================================================================================"
  echo "#"
  echo "# bbbeasy Configuration Utility for BBBEasy - Version $GIT_VERSION on $GIT_BRANCH branch"
  echo "#"
  echo "#    bbbeasy [options]"
  echo "#"
  echo "# Configuration:"
  echo "#    --version                        Display BBBEasy version"
  echo "#    --selfinstall                    Make bbbeasy runnable from anywhere"
  echo "#    --enableinstaller                Enable BBBEasy installer app"
  echo "#    --enableweb                      Enable BBBEasy Web app"
  echo "#"
  echo "# Development:"
  echo "#    --enabletests                    Enable running unit tests"
  echo "#    --test <-c> <name>               Run unit tests with a test name. Use for -c coverage"
  echo "#    --fix                            Fix php code style"
  echo "#    --migrate                        Run database migrations"
  echo "#    --metrics                        Generates code metrics"
  echo "#"
  echo "# Monitoring:"
  echo "#    --check                          Check configuration files and processes for problems"
  echo "#"
  echo "# Administration:"
  echo "#    --restart                        Restart BBBEasy"
  echo "#    --stop                           Stop BBBEasy"
  echo "#    --start                          Start BBBEasy"
  echo "#    --clean                          Restart and clean all log files"
  echo "#    --cleansessions                  Cleans sessions from the database"
  echo "#    --status                         Display running status of components"
  echo "#    --zip                            Zip up log files for reporting an error"
  echo "#========================================================================================================"
  echo
}

#
# Check file
#
check_file() {
  if [ ! -f "$1" ]; then
    echo "✘ File does not exist: $1"
    # m option means that the file is mandatory and the script cannot continue running
    if [ "$2" == "m" ]; then
      echo "✘ File $1 is mandatory. Script execution is interrupted"
      exit 1
    fi
  fi
}

need_production() {
  if [ "$ENVIRONMENT" != "production" ]; then
    echo "✘ Command can only be run in production environment"
    exit 1
  fi
}

#
# Display installed BBBEasy & servers version
#
validate_command() {
  if ! command -v "$1" &>/dev/null; then
    echo "✘ Error: Command $1 is not available. Please install it first."
    exit 1
  fi
}

display_version() {
  echo "■ BBBEasy     :  $GIT_VERSION"
  validate_command nginx
  echo "■ Nginx      :  $(nginx -v 2>&1)"
  validate_command php
  echo "■ PHP        :  $(php -v | sed -n 1p)"
  validate_command redis-server
  echo "■ Redis      :  $(redis-server --version)"
  validate_command psql
  echo "■ PostgreSQL :  $(psql -V)"
}

#
# Install bbbeasy to make runnable from anywhere
#
self_install() {
  if [ -f /usr/local/bin/bbbeasy ]; then
    echo "✘ bbbeasy already installed"
  else
    sudo ln -s "$SCRIPT" /usr/local/bin/bbbeasy
    echo "✔ bbbeasy successfully installed"
  fi
}

#
# Clean server status
#
validate_service() {
  systemctl is-active --quiet "$1" && echo "✔ $1 service is active" || echo "✘ $1 service is inactive"
}

display_status() {
  units="nginx php-fpm postgresql redis"
  echo
  line='————————————————————►'
  for unit in $units; do
    if [ $(pgrep -c "$unit") -ne 0 ]; then
      printf "%s %s [✔ - UP]\n" $unit "${line:${#unit}}"
    else
      printf "%s %s [✘ - DOWN]\n" $unit "${line:${#unit}}"
    fi
  done
  echo
}

#
# Clean BBBEasy cache
#
clean_cache() {
  echo "► Deleting cache"
  find "$BASEDIR/../bbbeasy-backend/bin/" ! -name '.gitkeep' -type f -exec rm -rfv {} \;
  find "$BASEDIR/../bbbeasy-backend/tmp/" ! -name '.gitkeep' -type f -exec rm -rfv {} \;
  find "$BASEDIR/../bbbeasy-backend/tmp/cache/" ! -name '.gitkeep' -type f -exec rm -rfv {} \;
  find "$BASEDIR/../bbbeasy-backend/tmp/mail/" -name '*.eml' -type f -exec rm -v {} \;
  find "$BASEDIR/../bbbeasy-backend/uploads/tmp/" -type f -exec rm -v {} \;
  find "$BASEDIR/../bbbeasy-backend/public/minified/" ! -name '.gitkeep' -type f -exec rm -v {} \;
  echo "✔ Cache deleted"
}

#
# Clean BBBEasy logs
#
clean_logs() {
  echo "► Cleaning logs"
  find "$BASEDIR/../bbbeasy-backend/logs/" ! -name '.gitkeep' -type f -exec rm -v {} \;
  echo "✔ Cleaned logs folder"
}

#
# Clean sessions from the database
#
clean_sessions() {
  sudo -u postgres psql -d bbbeasy -c "SELECT setval('users_sessions_id_seq'::regclass, 1);"
  sudo -u postgres psql -d bbbeasy -c "TRUNCATE users_sessions;"
}

#
# Archive logs for debugging
#
zip_logs() {
  echo "► Archiving logs"
  ARCHIVE="/home/$USER/logs/logs-$NOW.tar.gz"
  mkdir -p "/home/$USER/logs/"
  touch /tmp/empty
  tar cfv "$ARCHIVE" /tmp/empty
  tar rfv "$ARCHIVE" "$BASEDIR/../logs/" --exclude='.gitkeep'
  sudo tar rfv "$ARCHIVE" /var/log/nginx/
  echo "✔ Logs archived at $ARCHIVE"
}

#
# Run PHP Code Style Fixer
#
fix_styles() {
  echo "► Running PHP Code Style Fixer"
  cd "$BASEDIR/../bbbeasy-backend/"
  PHP_CS_FIXER_IGNORE_ENV=true composer csfix
  echo "✔ PHP Code Style Fixed"
}

#
# Enable backend installer app
#
validate_file() {
  if [ ! -f "$1" ]; then
    echo "✘ Error: $1 does not exist."
    exit 1
  fi
}

enable_installer() {
  echo "► Enabling backend installer app"
  validate_file "$BASEDIR/../bbbeasy-backend/app/config/config-$ENVIRONMENT.ini"
  if [ "$ENVIRONMENT" == "production" ]; then
    if grep -q -v "config.extension" "$BASEDIR/../bbbeasy-backend/app/config/config-$ENVIRONMENT.ini"; then
      echo "config.extension = -install" >>"$BASEDIR/../bbbeasy-backend/app/config/config-$ENVIRONMENT.ini"
      sudo sed -i 's/webapp/installer/g' /etc/nginx/sites-available/bbbeasy
      sudo service nginx reload
      printf "%s %s [✔ - DONE]\n"
    fi
  else
    if [ $(grep -c "config.extension =" "$BASEDIR/../bbbeasy-backend/app/config/config-$ENVIRONMENT.ini") -ne 0 ]; then
      # c cmd to replace the matched line in one go
      sed -i '/config.extension =/ c config.extension = -install' "$BASEDIR/../bbbeasy-backend/app/config/config-$ENVIRONMENT.ini"
      printf "%s %s [✔ - DONE]\n"
    fi
  fi
  echo "✔ Installer app enabled"
}

#
# Enable backend web app
#
enable_web() {
  echo "► Enabling backend webapp"
  if [ "$ENVIRONMENT" == "production" ]; then
    sed -i '/^config\.extension/d' "$BASEDIR/../bbbeasy-backend/app/config/config-$ENVIRONMENT.ini"
    sudo sed -i 's/installer/webapp/g' /etc/nginx/sites-available/bbbeasy
    sudo service nginx reload
    printf "%s %s [✔ - DONE]\n"
  else
    echo "► Enabling backend web app"
    if [ $(grep -c "config.extension = -install" "$BASEDIR/../bbbeasy-backend/app/config/config-$ENVIRONMENT.ini") -ne 0 ]; then
      sed -i '/config.extension = -install/ c config.extension =' "$BASEDIR/../bbbeasy-backend/app/config/config-$ENVIRONMENT.ini"
      printf "%s %s [✔ - DONE]\n"
    fi
  fi
  echo "✔ Web app enabled"
}

#
# Enable unit tests
#
enable_tests() {
  echo "► Enabling unit tests"
  rm -rfv "$BASEDIR/../bbbeasy-backend/public/statera/"
  mkdir -pv "$BASEDIR/../bbbeasy-backend/public/statera/coverage/"
  mkdir -pv "$BASEDIR/../bbbeasy-backend/public/statera/result/"
  cp -Rv "$BASEDIR/../bbbeasy-backend/tools/statera.php" "$BASEDIR/../bbbeasy-backend/public/statera/index.php"
  cp -Rv "$BASEDIR/../bbbeasy-backend/tests/ui/css" "$BASEDIR/../bbbeasy-backend/public/statera/"
  cp -Rv "$BASEDIR/../bbbeasy-backend/tests/ui/images" "$BASEDIR/../bbbeasy-backend/public/statera/"
  cp -Rv "$BASEDIR/../bbbeasy-backend/tests/ui/css" "$BASEDIR/../bbbeasy-backend/public/statera/result"
  cp -Rv "$BASEDIR/../bbbeasy-backend/tests/ui/images" "$BASEDIR/../bbbeasy-backend/public/statera/result"
  cp "$BASEDIR/../bbbeasy-backend/vendor/bcosca/fatfree-core/code.css" "$BASEDIR/../bbbeasy-backend/public/statera/css/code.css"
  cp "$BASEDIR/../bbbeasy-backend/vendor/bcosca/fatfree-core/code.css" "$BASEDIR/../bbbeasy-backend/public/statera/result/css/code.css"
  echo "✔ Unit tests enabled"
}

#
# Run unit test in CLI mode
#
run_tests() {
  echo "► Running unit tests"
  sudo phpenmod -s cli xdebug
  COVERAGE=$1
  USE_COVERAGE=""
  TEST_NAME="all"
  if [[ "$1" != "-c" ]]; then
    if [[ "$1" != "" ]]; then
      TEST_NAME=$1
    fi
    if [[ "$2" == "-c" ]]; then
      USE_COVERAGE="=withCoverage"
    fi
  fi
  if [[ "$1" == "-c" ]]; then
    USE_COVERAGE="=withCoverage"
    if [[ "$2" != "" ]]; then
      TEST_NAME=$2
    fi
  fi
  cd "$(dirname "$BASEDIR")/bbbeasy-backend/"
  # Revert database base to the initial state then create it again
  "vendor/bin/phinx" rollback -e testing -t 0 -vvv
  "vendor/bin/phinx" migrate -e testing -vvv
  cd "public"
  php index.php "/api?statera$USE_COVERAGE&test=$TEST_NAME" -o="../public/statera/result/index.html"
  SUCCESS=$(cat "statera/test.result")
  if [[ "$SUCCESS" == "success" ]]; then
    echo "✔ Test success"
    exit 0
  else
    echo "✘ Test fail"
    exit 1
  fi
}

deploy_apps() {
  install_dependencies
  chown_folders
  chmod_folders
  run_migrations
}

#
# Updates composer and reset user home composer ownership
#
update_composer() {
  echo "► Updating composer"
  sudo composer selfupdate
  sudo chown -R "$USER:$USER" "/home/$USER/.composer/"
}

# Install composer dependencies
#
install_dependencies() {
  # Backend
  cd "$(dirname "$BASEDIR")/bbbeasy-backend/"
  echo "► Updating composer dependencies"
  COMPOSER_ALLOW_SUPERUSER=1 composer install -o --no-dev
  sudo mkdir -p "$(dirname "$BASEDIR")/bbbeasy-frontend/build/"
  # Installer
  cd "$(dirname "$BASEDIR")/bbbeasy-frontend/"
  rm -rf dist/
  yarn cache clean
  NODE_ENV=production yarn install
  yarn build-installer
  cp -a "$BASEDIR/../bbbeasy-frontend/dist/." "$BASEDIR/../bbbeasy-frontend/build/installer/"
  # Installer
  cd "$(dirname "$BASEDIR")/bbbeasy-frontend/"
  rm -rf dist/
  yarn cache clean
  NODE_ENV=production yarn install
  yarn build
  cp -a "$BASEDIR/../bbbeasy-frontend/dist/." "$BASEDIR/../bbbeasy-frontend/build/webapp/"
  # Docs
  cd "$(dirname "$BASEDIR")/bbbeasy-docs/"
  yarn clear
  NODE_ENV=production yarn install
  yarn docusaurus clear
  yarn build
}

#
# Run database migrations
#
run_migrations() {
  echo "► Running database migration"
  cd "$(dirname "$BASEDIR")/bbbeasy-backend/"
  vendor/bin/phinx migrate -e "$ENVIRONMENT"
}

#
# Generate code quality metrics
#
validate_tool() {
  if ! command -v "$1" &>/dev/null; then
    echo "✘ Error: $1 is not installed. Please install it to continue."
    exit 1
  fi
}

generate_metrics() {
  validate_tool phpmetrics
  vendor/bin/phpmetrics --report-html="$BASEDIR/../bbbeasy-backend/public/metrics" "$BASEDIR/../bbbeasy-backend/app/src/"
}

chown_folders() {
  sudo chown -R www-data: "$BASEDIR/../bbbeasy-frontend/build/installer/"
  sudo chown -R www-data: "$BASEDIR/../bbbeasy-frontend/build/webapp/"
  sudo chown -R www-data: "$BASEDIR/../bbbeasy-docs/build/"
  sudo chown -R www-data: "$BASEDIR/../bbbeasy-backend"
}

#
# Give folders right permissions
#
chmod_folders() {
  cd "$APP_DIR"
  sudo chmod -R 755 -R "$BASEDIR/../bbbeasy-backend/data/"
  sudo chmod -R 766 -R "$BASEDIR/../bbbeasy-backend/logs/"
  sudo chmod -R 766 -R "$BASEDIR/../bbbeasy-backend/tmp/"
  sudo chmod -R 755 -R "$BASEDIR/../bbbeasy-backend/uploads/"
  sudo chmod -R 755 -R "$BASEDIR/../bbbeasy-backend/public/minified/"
}

#
# Start services
#
start_services() {
  sudo service postgresql start
  sudo service redis start
  sudo service nginx start
  sudo service php8.2-fpm start
}

#
# Stop services
#
stop_services() {
  sudo service postgresql stop
  sudo service redis stop
  sudo service nginx stop
  sudo service php8.2-fpm stop
}

#
# Restart services
#
restart_services() {
  sudo service postgresql restart
  sudo service redis restart
  sudo service nginx restart
  sudo service php8.2-fpm restart
}

run() {
  if [[ $# -eq 0 ]]; then
    usage
    exit 1
  fi
  # Environment
  HOST_TESTER=$(hostname)
  if [[ "$HOST_TESTER" == "bbbeasy.test" ]]; then
    ENVIRONMENT="development"
  else
    ENVIRONMENT="production"
  fi
  echo "► Detected environment: \`$ENVIRONMENT\`"
  while [[ $# -gt 0 ]]; do
    if [ "$1" = "--version" -o "$1" = "-version" -o "$1" = "-v" ]; then
      display_version
      shift
      continue
    fi
    if [ "$1" = "--selfinstall" -o "$1" = "-selfinstall" -o "$1" = "-si" ]; then
      self_install
      shift
      continue
    fi
    if [ "$1" = "--enableinstaller" -o "$1" = "-enableinstaller" -o "$1" = "-ei" ]; then
      enable_installer
      shift
      continue
    fi
    if [ "$1" = "--enableweb" -o "$1" = "-enableweb" -o "$1" = "-ew" ]; then
      enable_web
      shift
      continue
    fi
    if [ "$1" = "--enabletests" -o "$1" = "-enabletests" -o "$1" = "-e" ]; then
      enable_tests
      shift
      continue
    fi
    if [ "$1" = "--test" -o "$1" = "-test" -o "$1" = "-t" ]; then
      fix_styles
      run_tests "$2" "$3"
      shift
      shift
      continue
    fi
    if [ "$1" = "--check" -o "$1" = "-check" ]; then
      # todo: nginx config, mysql config, directories permissions...
      echo "not implemented yet"
      shift
      continue
    fi
    if [ "$1" = "--fix" -o "$1" = "-fix" -o "$1" = "-f" ]; then
      fix_styles
      shift
      continue
    fi
    if [ "$1" = "--migrate" -o "$1" = "-migrate" -o "$1" = "-m" ]; then
      run_migrations
      shift
      continue
    fi
    if [ "$1" = "--metrics" -o "$1" = "-metrics" -o "$1" = "-me" ]; then
      generate_metrics
      shift
      continue
    fi
    if [ "$1" = "--clean" -o "$1" = "-clean" ]; then
      clean_cache
      clean_logs
      shift
      continue
    fi
    if [ "$1" = "--cleansessions" -o "$1" = "-cleansessions" -o "$1" = "-cs" ]; then
      clean_sessions
      shift
      continue
    fi
    if [ "$1" = "--deploy" -o "$1" = "-deploy" -o "$1" = "-d" ]; then
      deploy_apps
      shift
      continue
    fi
    if [ "$1" = "--restart" -o "$1" = "-restart" -o "$1" = "-r" ]; then
      restart_services
      shift
      continue
    fi
    if [ "$1" = "--stop" -o "$1" = "-stop" -o "$1" = "-sp" ]; then
      stop_services
      shift
      continue
    fi
    if [ "$1" = "--start" -o "$1" = "-start" -o "$1" = "-sr" ]; then
      start_services
      shift
      continue
    fi
    if [ "$1" = "--status" -o "$1" = "-status" ]; then
      display_status
      shift
      continue
    fi
    if [ "$1" = "--zip" -o "$1" = "-zip" -o "$1" = "-z" ]; then
      zip_logs
      shift
      continue
    fi
    usage
    exit 1
  done
}

run "$@" #2>&1 | tee -a "$BASEDIR/../logs/bbbeasy-$NOW.log"
